package com.zk.config.api.client;

import java.io.IOException;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
import com.zk.config.api.constants.Constants;
import com.zk.config.api.service.ZkCache;
import lombok.Getter;

public class ConfigClient {
	private static Logger logger = Logger.getLogger(ConfigClient.class);
	
	private ZooKeeper zk;
	
	private Watcher watcher;
	@Getter
	private boolean isClose = true;
	@Getter
	public ZkCache zkCache = new ZkCache();
	
	private TimeThread timeThread;
	
	public ConfigClient(String zkHost, int connectTimeout) {
		this(zkHost, connectTimeout, 0, false, null, null);
	}
	
	public ConfigClient(String zkHost, int connectTimeout, Properties properties) {
		this(zkHost, connectTimeout, 0, false, properties, null);
	}
	
	public ConfigClient(String zkHost, int connectTimeout, long refreshDataTime, boolean watch) {
		this(zkHost, connectTimeout, refreshDataTime, watch, null, null);
	}
	
	public ConfigClient(String zkHost, int connectTimeout, long refreshDataTime, boolean watch, Properties properties) {
		this(zkHost, connectTimeout, refreshDataTime, watch, properties, null);
	}
	
	public ConfigClient(String zkHost, int connectTimeout, long refreshDataTime, boolean watch, Properties properties, String authInfo) {
		Constants.zkHost = zkHost;
		Constants.connectTimeout = connectTimeout;
		Constants.refreshDataTime = refreshDataTime;
		Constants.watch = watch;
		Constants.authInfo = authInfo;
		if(ZkCache.properties == null) {
			ZkCache.properties=properties;
		}
		
		if (Constants.watch) {
			this.watcher = new Watcher() {
				@Override
				public void process(WatchedEvent event) {
					 if (event.getState() == KeeperState.Expired) {
						 if (zk != null) {
							 try {
								 zk.close();
							 } catch (InterruptedException e) {
								 e.printStackTrace();
							 }
							 zk = null;
						 }
						 isClose = true;
						 if(Constants.watch) {
							 zkCache.reloadWatcher(Constants.zkRoot);
						 }
					 }
					String path = event.getPath();
					if (event.getType() != EventType.None) {
						try {
							if(event.getType() == EventType.NodeDeleted) {
								zkCache.reloadIfNodeDelete(path);
							} else if (event.getType() == EventType.NodeCreated || event.getType() == EventType.NodeDataChanged) {
								try {
									zk.exists(path, true); // 回调继续监听监听变更的路径节点
								} catch (KeeperException | InterruptedException e) {
									e.printStackTrace();
									logger.error(e.getMessage());
									// 失败重试
									Thread.sleep(200);
									zk.exists(path, true);
								}
								zkCache.reloadIfNodeCreateOrChange(path);
							} else if(event.getType() == EventType.NodeChildrenChanged) {
								zkCache.read(path, true);
							}
						} catch (Exception e) {
							e.printStackTrace();
							logger.error(e.getMessage());
							zkCache.read(path, true);
						}
					}
				}
			};
		}
		if(Constants.refreshDataTime > 0) {
			timeThread = new TimeThread(Constants.refreshDataTime);
			timeThread.start();
		}
	}
	
	public void setLog4jPathName(String log4jPathName){
		Constants.log4jPathName = log4jPathName;
	}
	
	public void setMapingToLocalTag(String mapingToLocalTag){
		Constants.mapingToLocalTag = mapingToLocalTag;
	}
	
	public void setMapingDir(String mapingDir){
		Constants.mapingDir = mapingDir;
	}
	
	public ZooKeeper getZk() {
		if(zk == null) {
			synchronized (ConfigClient.class) {
				try {
					String newZkHost = Constants.zkHost.replaceAll("/+", "/").replaceAll("/$", "");
					zk = new ZooKeeper(newZkHost, Constants.connectTimeout, watcher);
					if(Constants.authInfo != null && Constants.authInfo.length() > 0) {
						zk.addAuthInfo("digest", Constants.authInfo.getBytes());
					}
					zkCache.setZk(zk);
					isClose = false;
				} catch (IOException e) {
					e.printStackTrace();
					logger.error(e.getMessage());
				}
			}
		}
		return zk;
	}

	public void setZk(ZooKeeper zk) {
		this.zk = zk;
	}
	
	public void close(){
		try {
			if (zk != null) {
				zk.close();
				zk = null;
			}
			isClose = true;
			if (timeThread != null) {
				timeThread.close();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
			logger.error(e.getMessage());
		}
	}
	
	public void init() {
		zkCache.setZk(getZk());
		zkCache.init();
	}
	
	class TimeThread extends Thread {
		private long time;
		private boolean runflag = true;
		public TimeThread(long time) {
			this.time = time;
		}
		public void run() {
			while(runflag) {
				try {
					Thread.sleep(time);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if(runflag) {
					zkCache.refresh();
				}
			}
		}
		public void close(){
			runflag = false;
			this.interrupt();
		}
	}
}
